package org.dicr.util.jds.gui;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import org.dicr.event.*;
import org.dicr.gui.*;
import org.dicr.gui.res.*;
import org.dicr.util.jds.*;

import com.borland.dx.dataset.*;
import com.borland.dx.sql.dataset.*;

/***********************************************************************************************************************
 * @author Igor A Tarasov me@dicr.dn.ua
 * @version 030128
 **********************************************************************************************************************/
public class DataControlPanel extends ControlPanel implements DataSetAware {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	protected JButton firstButton = new JButton();

	protected JButton lastButton = new JButton();

	protected JButton prevButton = new JButton();

	protected JButton nextButton = new JButton();

	protected JButton addButton = new JButton();

	protected JButton delButton = new JButton();

	protected JButton postButton = new JButton();

	protected JButton canselButton = new JButton();

	protected JButton saveButton = new JButton();

	protected JButton refreshButton = new JButton();

	protected JButton[] buttons = new JButton[] { this.firstButton, this.prevButton, this.addButton, this.delButton,
	        this.nextButton, this.lastButton, this.postButton, this.canselButton, this.saveButton, this.refreshButton };

	public static final int BUTTON_FIRST = 1;

	public static final int BUTTON_LAST = 2;

	public static final int BUTTON_PREV = 4;

	public static final int BUTTON_NEXT = 8;

	public static final int BUTTON_ADD = 16;

	public static final int BUTTON_DEL = 32;

	public static final int BUTTON_POST = 64;

	public static final int BUTTON_CANSEL = 128;

	public static final int BUTTON_SAVE = 256;

	public static final int BUTTON_REFRESH = 512;

	/** битовая маска видимых элементов управления */
	protected int controlMask = -1;

	/** управляемый источник данных */
	protected DataSet dataset = null;

	/** база данных SQL */
	protected Database database = null;

	/** слушатель событий данных */
	protected DataListener dataListener = new DataListener();

	/** информация о состоянии данных */
	protected DataStateInfo dataInfo = new DataStateInfo();

	/*******************************************************************************************************************
	 * Конструктор
	 ******************************************************************************************************************/
	public DataControlPanel() {
		try {
			jbInit();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
	}

	/*******************************************************************************************************************
	 * Инициализация
	 ******************************************************************************************************************/
	private void jbInit() {
		Insets margin = new Insets(0, 0, 0, 0);
		for (int i = 0; i < this.buttons.length; i++) {
			JButton button = this.buttons[i];
			button.setMargin(margin);
			this.add(button);
		}
		this.firstButton.setAction(new FirstButtonAction());
		this.lastButton.setAction(new LastButtonAction());
		this.prevButton.setAction(new PrevButtonAction());
		this.nextButton.setAction(new NextButtonAction());
		this.addButton.setAction(new AddButtonAction());
		this.delButton.setAction(new DelButtonAction());
		this.postButton.setAction(new PostButtonAction());
		this.canselButton.setAction(new CanselButtonAction());
		this.saveButton.setAction(new SaveButtonAction());
		this.refreshButton.setAction(new RefreshButtonAction());
	}

	/*******************************************************************************************************************
	 * Устанавливает битовую маску набора элементов управления
	 ******************************************************************************************************************/
	public void setControlButtonsMask(int mask) {
		if (mask != this.controlMask) {
			this.controlMask = mask;
			this.firstButton.setVisible((mask & BUTTON_FIRST) != 0);
			this.lastButton.setVisible((mask & BUTTON_LAST) != 0);
			this.prevButton.setVisible((mask & BUTTON_PREV) != 0);
			this.nextButton.setVisible((mask & BUTTON_NEXT) != 0);
			this.addButton.setVisible((mask & BUTTON_ADD) != 0);
			this.delButton.setVisible((mask & BUTTON_DEL) != 0);
			this.postButton.setVisible((mask & BUTTON_POST) != 0);
			this.canselButton.setVisible((mask & BUTTON_CANSEL) != 0);
			this.saveButton.setVisible((mask & BUTTON_SAVE) != 0);
			this.refreshButton.setVisible((mask & BUTTON_REFRESH) != 0);
		}
	}

	/*******************************************************************************************************************
	 * Возвращает битовую маску элементов управления
	 ******************************************************************************************************************/
	public int getControlButtonMask() {
		return this.controlMask;
	}

	/*******************************************************************************************************************
	 * Обновляет информацию о состоянии данных
	 ******************************************************************************************************************/
	protected void updateDataStateInfo() {
		if (this.dataset != null && this.dataset.isOpen()) {
			this.dataInfo.opened = true;
			this.dataInfo.connected = (this.database != null) ? this.database.isOpen() : true;
			this.dataInfo.empty = this.dataset.isEmpty();
			this.dataInfo.atFirst = this.dataset.atFirst();
			this.dataInfo.atLast = this.dataset.atLast();
			this.dataInfo.editing = this.dataset.isEditing();
			this.dataInfo.editable = DataMisc.isDataSetEditable(this.dataset);
			this.dataInfo.enableInsert = this.dataset.isEnableInsert();
			this.dataInfo.enableDelete = this.dataset.isEnableDelete();
			this.dataInfo.saveChangesSupported = this.dataset.saveChangesSupported();
			this.dataInfo.refreshSupported = this.dataset.refreshSupported();
		} else {
			this.dataInfo.opened = false;
			this.dataInfo.connected = false;
			this.dataInfo.empty = true;
			this.dataInfo.atFirst = true;
			this.dataInfo.atLast = true;
			this.dataInfo.editing = false;
			this.dataInfo.editable = false;
			this.dataInfo.enableInsert = false;
			this.dataInfo.enableDelete = false;
			this.dataInfo.saveChangesSupported = false;
			this.dataInfo.refreshSupported = false;
		}
	}

	/*******************************************************************************************************************
	 * Обновляет состояние кнопок связи с базой данных
	 ******************************************************************************************************************/
	protected void updateButtons() {
		if (this.controlMask == 0) return;
		if (this.dataInfo.opened) {
			this.firstButton.setEnabled(!this.dataInfo.empty && !this.dataInfo.atFirst && !this.dataInfo.editing);
			this.lastButton.setEnabled(!this.dataInfo.empty && !this.dataInfo.atLast && !this.dataInfo.editing);
			this.prevButton.setEnabled(!this.dataInfo.empty && !this.dataInfo.atFirst && !this.dataInfo.editing);
			this.nextButton.setEnabled(!this.dataInfo.empty && !this.dataInfo.atLast && !this.dataInfo.editing);
			this.addButton.setEnabled(this.dataInfo.enableInsert && this.dataInfo.editable && !this.dataInfo.editing);
			this.delButton.setEnabled(this.dataInfo.enableDelete && this.dataInfo.editable && !this.dataInfo.empty
			        && !this.dataInfo.editing);
			this.postButton.setEnabled(this.dataInfo.editing);
			this.canselButton.setEnabled(this.dataInfo.editing);
			this.saveButton.setEnabled(this.dataInfo.saveChangesSupported && this.dataInfo.connected
			        && this.dataInfo.editable && !this.dataInfo.editing);
			this.refreshButton.setEnabled(this.dataInfo.refreshSupported && this.dataInfo.connected
			        && !this.dataInfo.editing);
		} else {
			this.firstButton.setEnabled(false);
			this.lastButton.setEnabled(false);
			this.prevButton.setEnabled(false);
			this.nextButton.setEnabled(false);
			this.addButton.setEnabled(false);
			this.delButton.setEnabled(false);
			this.postButton.setEnabled(false);
			this.canselButton.setEnabled(false);
			this.saveButton.setEnabled(false);
			this.refreshButton.setEnabled(false);
		}
	}

	/*******************************************************************************************************************
	 * Устанавливает источник данных
	 ******************************************************************************************************************/
	@Override
    public void setDataSet(DataSet dataSet) throws DataSetException {
		if (this.dataset != dataSet) try {
			if (this.database != null) {
				this.database.removeConnectionUpdateListener(this.dataListener);
				this.database = null;
			}
			if (this.dataset != null) {
				this.dataset.removeAccessListener(this.dataListener);
				this.dataset.removeNavigationListener(this.dataListener);
				this.dataset.removeStatusListener(this.dataListener);
				this.dataset.removeDataChangeListener(this.dataListener);
			}
			this.dataset = dataSet;
			if (this.dataset != null) {
				StorageDataSet stor = this.dataset.getStorageDataSet();
				if (stor != null && (stor instanceof QueryDataSet)) {
					QueryDataSet quer = (QueryDataSet) stor;
					this.database = quer.getDatabase();
					if (this.database != null) {
						this.database.addConnectionUpdateListener(this.dataListener);
					}
				}
				this.dataset.addAccessListener(this.dataListener);
				this.dataset.addNavigationListener(this.dataListener);
				this.dataset.addStatusListener(this.dataListener);
				this.dataset.addDataChangeListener(this.dataListener);
				this.dataset.open();
			}
		} finally {
			updateDataStateInfo();
			updateButtons();
		}
	}

	/*******************************************************************************************************************
	 * Возвращает источник данных
	 ******************************************************************************************************************/
	@Override
    public DataSet getDataSet() {
		return this.dataset;
	}

	/*******************************************************************************************************************
	 * Возвращает заданную маской кнопку. Этим методом пользуются для того чтобы добавить/изменить обработчика событий
	 * от кнопки
	 ******************************************************************************************************************/
	public JButton getButton(int buttonMask) {
		switch (buttonMask) {
			case BUTTON_FIRST:
				return this.firstButton;
			case BUTTON_LAST:
				return this.lastButton;
			case BUTTON_PREV:
				return this.prevButton;
			case BUTTON_NEXT:
				return this.nextButton;
			case BUTTON_ADD:
				return this.addButton;
			case BUTTON_DEL:
				return this.delButton;
			case BUTTON_POST:
				return this.postButton;
			case BUTTON_CANSEL:
				return this.canselButton;
			case BUTTON_SAVE:
				return this.saveButton;
			case BUTTON_REFRESH:
				return this.refreshButton;
			default:
				throw new IllegalArgumentException("Неверный аргумент butonMask");
		}
	}

	/*******************************************************************************************************************
	 * ######################################################################### Обработчики событий кнопок
	 * #########################################################################
	 ******************************************************************************************************************/
	// //////////////////////////////////////////////////////////////////////////
	public class FirstButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public FirstButtonAction() {
			Icon icon = ResourceManager.getIcon("ToStart24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "<<" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Перейти к первому элементу списка");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) DataControlPanel.this.dataset.first();
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class LastButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public LastButtonAction() {
			Icon icon = ResourceManager.getIcon("ToEnd24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? ">>" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Перейти к последнему элементу списка");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) DataControlPanel.this.dataset.last();
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class PrevButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public PrevButtonAction() {
			Icon icon = ResourceManager.getIcon("StepBack24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "<" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Перейти к предыдущему элементу списка");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) DataControlPanel.this.dataset.prior();
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class NextButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public NextButtonAction() {
			Icon icon = ResourceManager.getIcon("StepForward24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? ">" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Перейти к следующему элементу списка");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) DataControlPanel.this.dataset.next();
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class AddButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public AddButtonAction() {
			Icon icon = ResourceManager.getIcon("New24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "+" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Добавить элемент в список");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					DataControlPanel.this.dataset.last();
					DataControlPanel.this.dataset.insertRow(false);
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class DelButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public DelButtonAction() {
			Icon icon = ResourceManager.getIcon("Delete24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "-" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Удалить элемент из списка");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					if (DataControlPanel.this.dataset.isEditingNewRow()
					        || JOptionPane.showConfirmDialog(DataControlPanel.this.delButton,
					                "Удалить элемент и связанные с ним данные из базы данных ?", "Удаление элемента",
					                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
						DataControlPanel.this.dataset.deleteRow();
					}
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class PostButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public PostButtonAction() {
			Icon icon = ResourceManager.getIcon("Up24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "^" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Принять изменения");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					DataControlPanel.this.dataset.post();
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class CanselButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public CanselButtonAction() {
			Icon icon = ResourceManager.getIcon("Undo24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "X" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Отменить изменения");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					DataControlPanel.this.dataset.cancel();
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class SaveButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public SaveButtonAction() {
			Icon icon = ResourceManager.getIcon("Save24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "S" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Отправить изменения");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					DataControlPanel.this.dataset.saveChanges();
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////////
	public class RefreshButtonAction extends AbstractAction {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public RefreshButtonAction() {
			Icon icon = ResourceManager.getIcon("Refresh24.gif");
			super.putValue(Action.SMALL_ICON, icon);
			super.putValue(Action.NAME, (icon == null) ? "R" : null);
			super.putValue(Action.SHORT_DESCRIPTION, "Получить изменения");
		}

		@Override
        public void actionPerformed(ActionEvent e) {
			try {
				if (DataControlPanel.this.dataset != null && DataControlPanel.this.dataset.isOpen()) {
					boolean refresh = true;
					if (DataMisc.isModified(DataControlPanel.this.dataset)) {
						int ret = JOptionPane.showConfirmDialog(DataControlPanel.this.refreshButton, new String[] {
						        "Все несохраненные изменения будут утеряны.",
						        "Сохранить изменения в перед обновлением ?" }, "Обновление данных",
						        JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
						switch (ret) {
							case JOptionPane.YES_OPTION:
								DataControlPanel.this.dataset.saveChanges();
								break;
							case JOptionPane.CANCEL_OPTION:
								refresh = false;
								break;
						}
					}
					if (refresh) DataControlPanel.this.dataset.refresh();
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			}
		}
	}

	/*******************************************************************************************************************
	 * ######################################################################### Обработчик событий данных
	 * #########################################################################
	 ******************************************************************************************************************/
	class DataListener extends ConnectionUpdateAdapter implements AccessListener, NavigationListener, StatusListener, DataChangeListener {

		/***************************************************************************************************************
		 * Обработка событий доступа к данным
		 **************************************************************************************************************/
		@Override
        public void accessChange(AccessEvent event) {
			updateDataStateInfo();
			updateButtons();
		}

		/***************************************************************************************************************
		 * Обработка событий навигации
		 **************************************************************************************************************/
		@Override
        public void navigated(NavigationEvent event) {
			updateDataStateInfo();
			updateButtons();
		}

		/***************************************************************************************************************
		 * Обработка событий состояния источника данных
		 **************************************************************************************************************/
		@Override
        public void statusMessage(StatusEvent event) {
			switch (event.getCode()) {
				case (StatusEvent.EDIT_STARTED):
				case (StatusEvent.EDIT_CANCELED):
				case (StatusEvent.LOCATE_MATCH_FOUND):
				case (StatusEvent.LOCATE_MATCH_NOT_FOUND):
				case (StatusEvent.DATA_CHANGE):
					updateDataStateInfo();
					updateButtons();
					break;
			}
		}

		/***************************************************************************************************************
		 * Обработчик события смены статуса соединения
		 **************************************************************************************************************/
		@Override
		public void connectionChanged(ConnectionUpdateEvent event) {
			updateDataStateInfo();
			updateButtons();
		}

		/***************************************************************************************************************
		 * Обработчик закрытия соединения
		 **************************************************************************************************************/
		@Override
		public void connectionClosed(ConnectionUpdateEvent event) {
			updateDataStateInfo();
			updateButtons();
		}

		/***************************************************************************************************************
		 * Обработчик события изменения данных
		 **************************************************************************************************************/
		@Override
        public void dataChanged(DataChangeEvent e) {
			updateDataStateInfo();
			updateButtons();
		}

		/***************************************************************************************************************
		 * Обработчик изменения данных
		 **************************************************************************************************************/
		@Override
        public void postRow(DataChangeEvent e) {
		//
		}
	}

	/*******************************************************************************************************************
	 * ######################################################################### Информация о состоянии источника данных
	 * #########################################################################
	 ******************************************************************************************************************/
	public class DataStateInfo {
		public boolean opened = false;

		public boolean connected = false;

		public boolean empty = false;

		public boolean atFirst = false;

		public boolean atLast = false;

		public boolean editing = false;

		public boolean editable = false;

		public boolean enableInsert = false;

		public boolean enableDelete = false;

		public boolean saveChangesSupported = false;

		public boolean refreshSupported = false;
	}
}
